package com.googlecode.jfxgap;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.HBoxBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

/**
 *
 * Licence unclear, may needs to be rewritten
 *
 * Javafx WebView Dialog utility.
 * <p/>
 * Usage:
 * <pre>{@code
webEngine.setPromptHandler(new Callback<PromptData, String>() {
@Override
public String call(PromptData arg0) {
JavaScriptDialog dialog = new JavaScriptDialog(stage, arg0.getMessage());
dialog.addPrompt(arg0.getDefaultValue());
dialog.addAcceptButton();
dialog.addRejectButton();
dialog.showAndWait();
return dialog.getPrompt();
}
});
 * }</pre>
 *
 * @author Andrew Taylor - andy@benow.ca
 */
public class JavaScriptDialog extends Stage {

    boolean accept = true;
    final Stage self = this;
    private TextField field;
    private Label label;
    private final BorderPane borderPane;
    private final VBox messageBox;
    private final HBox buttonBox;
    private Button acceptBtn;
    private Button rejectBtn;


    public JavaScriptDialog(final Stage primaryStage) {
        super(StageStyle.TRANSPARENT);

        super.centerOnScreen();
        //    super.initStyle(StageStyle.UTILITY);
        super.initModality(Modality.WINDOW_MODAL);
        super.setIconified(false);
        super.setResizable(false);
        super.initOwner(primaryStage);
        primaryStage.getScene().getRoot().setEffect(null);
        this.borderPane = new BorderPane();

        this.messageBox = new VBox();
        this.messageBox.setAlignment(Pos.CENTER_LEFT);
        this.borderPane.setCenter(this.messageBox);
        BorderPane.setAlignment(messageBox, Pos.CENTER);
        //    BorderPane.setMargin(messageBox, new Insets(MARGIN, MARGIN, MARGIN, 2 * MARGIN));

        this.buttonBox = new HBox();
        this.buttonBox.setAlignment(Pos.BOTTOM_RIGHT);
        this.buttonBox.getStyleClass().add("button-box");
        this.borderPane.setBottom(this.buttonBox);
        BorderPane.setAlignment(buttonBox, Pos.BOTTOM_RIGHT);

    }

    public Label getLabel() {
        return label;
    }

    /**
     * Add a reject (cancel) button with the default label (Cancel)
     * @return the added reject button
     */
    public Button addRejectButton() {
        return addRejectButton("Cancel");
    }

    public Label setMessage(String msg) {
        if (label == null) {
            this.label = new Label();
            this.label.setId("msg");
            messageBox.getChildren().add(this.label);
        }
        this.label.setText(msg);
        return this.label;
    }

    /**
     * Add a reject button with the given label.
     *
     * @param rejectText label for the button
     * @return the added button
     */
    public Button addRejectButton(String rejectText) {
        if (this.rejectBtn == null) {
            this.rejectBtn = new Button();
            this.rejectBtn.setCancelButton(true);
            this.rejectBtn.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent actionEvent) {
                    // abort action and close the this.
                    accept = false;
                    self.close();
                }
            });
            this.buttonBox.getChildren().add(this.rejectBtn);
        }
        this.rejectBtn.setText(rejectText);

        return this.rejectBtn;
    }

    /**
     * Add a prompt field with no default value.
     *
     * @return the created field
     */
    public TextField addPrompt() {
        return setPrompt(null);
    }

    /**
     * Add a prompt field with a given default value.
     * @param defaultValue the value to show in the field.
     * @return the created field.
     */
    public TextField setPrompt(String defaultValue) {
        if (field == null) {
            this.field = new TextField();
            this.messageBox.getChildren().add(this.field);
        }
        if (defaultValue != null)
            this.field.setText(defaultValue);

        return field;
    }

    /**
     * Add an accept button with the default label (Ok).
     *
     * @return the added button
     */
    public Button addAcceptButton() {
        return addAcceptButton("Ok");
    }

    /**
     * Add an accept button with the given label
     * @param acceptText the label for the button
     * @return the added button
     */
    public Button addAcceptButton(String acceptText) {
        if (acceptBtn == null) {
            this.acceptBtn = new Button();
            this.acceptBtn.setDefaultButton(true);
            this.acceptBtn.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent actionEvent) {
                    // take action and close the this.
                    accept = true;
                    getOwner().getScene().getRoot().setEffect(null);
                    self.close();
                }
            });
            this.buttonBox.getChildren().add(this.acceptBtn);
        }
        this.acceptBtn.setText(acceptText);
        return acceptBtn;
    }

    /**
     * Show the dialog and wait for completion (either through
     * cancel, hitting esc or accepting)
     */
    @Override
    public void showAndWait() {
        this.setScene(new Scene(HBoxBuilder.create().styleClass("modal-dialog").children(borderPane).build(),
            Color.TRANSPARENT));
           this.getScene().getStylesheets().add(getClass().getResource("/JavaScriptDialog.css").toExternalForm());

        //    this.label.setWrapText(true);
        //    this.label.setMaxWidth(500);
        //    this.label.setMaxHeight(400);

        // allow the dialog to be dragged around.
        final Node root = this.getScene().getRoot();
        final Delta dragDelta = new Delta();
        root.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                // record a delta distance for the drag and drop operation.
                dragDelta.x = self.getX() - mouseEvent.getScreenX();
                dragDelta.y = self.getY() - mouseEvent.getScreenY();
            }
        });
        root.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                self.setX(mouseEvent.getScreenX() + dragDelta.x);
                self.setY(mouseEvent.getScreenY() + dragDelta.y);
            }
        });

        super.showAndWait();
    }

    /**
     *
     * @return true if the dialog was accepted (the accept button pressed);
     */
    public boolean accepted() {
        return accept;
    }

    // records relative x and y co-ordinates.
    class Delta {
        double x, y;
    }

    /**
     * Gets the prompt which was entered.
     *
     * @return the entered prompt
     */
    public String getPrompt() {
        if (!accept)
            return null;
        return field.getText();
    }

    /**
     * Build and show a prompt dialog
     *
     * @param owner the owner of the dialog
     * @param message the message to show above the prompt (or null)
     * @param defaultValue the default value of the prompt (or null)
     * @return the provided prompt value
     */
    public static String showPrompt(Stage owner, String message, String defaultValue) {
        JavaScriptDialog dialog = new JavaScriptDialog(owner);
        if (message != null)
            dialog.setMessage(message);
        dialog.setPrompt(defaultValue);
        dialog.addAcceptButton();
        dialog.addRejectButton();
        dialog.showAndWait();
        return dialog.getPrompt();
    }

    /**
     * Build and show a confirmation dialog, returning true if confirmed.
     *
     * @param owner the owner stage
     * @param msg the message to show
     * @return true if confirmed (false otherwise)
     */
    public static boolean showConfirm(Stage owner, String msg) {
        JavaScriptDialog dialog = new JavaScriptDialog(owner);
        dialog.setMessage(msg);
        dialog.addAcceptButton();
        dialog.addRejectButton();
        dialog.showAndWait();
        return dialog.accepted();
    }

    /**
     * Build and show an alert dialog
     *
     * @param stage the owner stage
     * @param msg the message to show
     */
    public static void showAlert(Stage stage, String msg) {
        JavaScriptDialog dialog = new JavaScriptDialog(stage);
        dialog.setMessage(msg);
        dialog.addAcceptButton();
        dialog.showAndWait();
    }

}
